#ifndef GX_MATH3D_H
#define GX_MATH3D_H

#include "windows.h"
#include "math.h"
#include "limits.h"

const int MaxInt = INT_MAX;
const float NearZero = 1e-2f;  // A float whose magnitude less then this number is considered zero.
const float PI = 3.14159265358979323846f;

struct svec3
{
	float x,y,z;
};

class vec3 : public svec3
{
public:
	vec3()
	{
		x=0.0f; y=0.0f; z=0.0f;
	}
	vec3(float x0, float y0,float z0)
	{
		x=x0; y=y0; z=z0;
	};
	vec3 operator *(float u) const
	{
		vec3 rs = vec3(x*u,y*u,z*u);
		return rs;
	};
	vec3& operator *=(float f)
	{
		x*=f;
		y*=f;
		z*=f;
		return *this;
	};
	float operator *(const vec3 & v)
	{
		return (x*v.x + y*v.y + z*v.z); 
	};
	vec3 operator +(const vec3 & v)
	{
		vec3 rs = vec3(x+v.x,y+v.y,z+v.z);
		return rs;
	};
	vec3 operator - ()
	{
		vec3 rs;
		rs.x = -x;
		rs.y = -y;
		rs.z = -z;
		return rs;
	}
	float & operator[](int x)
	{
		return ((float*)this)[x];
	}
	friend vec3 operator -(const vec3 &lv,const vec3 &v)
	{
		vec3 rs = vec3(lv.x-v.x,lv.y-v.y,lv.z-v.z);
		return rs;
	}
	void operator /=(const vec3 & v)
	{
		x/=v.x;
		y/=v.y;
		z/=v.z;
	}
	void operator +=(const vec3 & v)
	{
		x += v.x;  y+=v.y;  z+=v.z;
	};
	void operator -=(const vec3 & v)
	{
		x-=v.x;
		y-=v.y;
		z-=v.z;
	}
	void operator *=(const vec3 &v)
	{
		x*=v.x;
		y*=v.y;
		z*=v.z;
	}
	vec3& operator =(const vec3 &v)
	{
		x= v.x; y=v.y; z=v.z;
		return *this;
	};
	bool operator ==(const vec3 &v)
	{
		return (fabs(x-v.x)<NearZero && fabs(y-v.y)<NearZero && fabs(z-v.z)<NearZero);
	}
	float GetLength()
	{
		return sqrt(x*x+y*y+z*z);
	}
	float GetLength2()
	{
		return x*x+y*y+z*z;
	}
};

struct svec2
{
	float x,y;
};

struct vec2 : public svec2
{
	vec2(float ax, float ay)
	{
		x = ax; y = ay;
	}
	vec2()
	{
		x = 0.0f; y = 0.0f;
	}
	vec2 &operator =(const vec2 &v)
	{
		x = v.x;
		y = v.y;
		return *this;
	}
	bool operator ==(const vec2 &v)
	{
		return (fabs(x-v.x)<NearZero && fabs(y-v.y)<NearZero);
	}
	vec2& operator -(const vec2 &v)
	{
		x -= v.x;
		y -= v.y;
		return *this;
	}
	vec2& operator +(const vec2& v)
	{
		x+=v.x; y+=v.y;
		return *this;
	}
};

struct svec4
{
	float x,y,z,w;
};

struct vec4 : public svec4
{
	vec4()
	{
		x=0.0f; y=0.0f; z=0.0f; w = 0.0f;
	}
	vec4(float x0, float y0,float z0, float w0)
	{
		x=x0; y=y0; z=z0; w= w0;
	};
	vec4 operator *(float u)
	{
		vec4 rs = vec4(x*u,y*u,z*u,w*u);
		return rs;
	};
	float operator *(vec4 v)
	{
		return (x*v.x + y*v.y + z*v.z +w*v.w); 
	};
	vec4 operator +(vec4 v)
	{
		vec4 rs = vec4(x+v.x,y+v.y,z+v.z,w+v.w);
		return rs;
	};
	void operator +=(vec4 v)
	{
		x += v.x;  y+=v.y;  z+=v.z; w+=v.w;
	};

};
union Matrix4
{
	float m[4][4]; //first index is column and second index is row;
	float values[16];
	struct
	{
		float _11,_12,_13,_14,
		  _21,_22,_23,_24,
		  _31,_32,_33,_34,
		  _41,_42,_43,_44;
	} mi;
	Matrix4()
	{
		mi._11=mi._12=mi._13=mi._14=
		  mi._21=mi._22=mi._23=mi._24=
		  mi._31=mi._32=mi._33=mi._34=
		  mi._41=mi._42=mi._43=mi._44=0.0f;
		mi._11 = mi._22 = mi._33 = mi._44 = 1.0f;
	};
};
struct Quaternion
{
	float x,y,z,w;
	Quaternion()
	{
		x =0.0f; y=0.0f; z=0.0f; w=1.0f;
	}
};

class Plane
{
public:
	float a,b,c,d;
	Plane();
	Plane(float aa, float ab, float ac, float ad);
	void Normalize();
	float PointDistance(vec3 &P);
	bool operator ==(const Plane &rhs);
};

void Normalize(vec3 &NIn, vec3 &NOut);
vec3 VectorCross(vec3 &v1, vec3& v2);

inline float PointPlaneDistance(vec3 &v, Plane &pl)
{
	return v.x*pl.a + v.y*pl.b + v.z*pl.c + pl.d;   
}

void PlaneFromVertices(Plane &p, const vec3 &v1, const vec3 &v2, const vec3 &v3);
int CalcMatrixInverse(Matrix4 &MIn, Matrix4 &MOut);
void MatrixTranspose(Matrix4 &MIn, Matrix4 &MOut);
void MatrixTransformVec4(Matrix4 &MIn, vec4 &VIn, vec4 &VOut);
void MatrixTransformVec3(Matrix4 &MIn, vec3 &VInm, vec3 &VOut);
void MatrixTransformNormal(Matrix4 &MIn, vec3 &NIn, vec3 &NOut);
void MatrixIdentity(Matrix4 &MIn);
void MatrixTranslation(Matrix4 &MIn, float x, float y, float z);
void MatrixReflection(Matrix4 &mat, const Plane & plane);
int MatrixMultiply(Matrix4 &M1, Matrix4 &M2, Matrix4 &MOut);
void MatrixScale(Matrix4 &MIn, float x, float y, float z);
void QuaternionNormalize(Quaternion &QIn, Quaternion &QOut);
void QuaternionMultiply(Quaternion &Q1, Quaternion &Q2, Quaternion &QOut);
void QuaternionToMatrix(Quaternion &QIn, Matrix4 &MOut);
void QuaternionSlerp(Quaternion &Q1, Quaternion &Q2, float t, Quaternion &res);
float Power(float base, float exponent);
int Round(float value); 

bool RayPlaneIntersection(vec3 &p0, vec3 &v, Plane &p, vec3 &vinter);
bool LinePlaneIntersection(vec3 &p0, vec3 &p1, Plane &p, vec3 &vinter, float &at);

void Vec3Interpolate(vec3 &vout, vec3 &v1, vec3 &v2, float t);
void Vec2Interpolate(vec2 &vout, vec2 &v1, vec2 &v2, float t);

#endif